//@author A0081058H
package net.mysoc.w111j.ui;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.util.Pair;
import net.mysoc.w111j.ui.main.MainPresenter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The entry point of the application. Setups the MainPresenter and launches the GUI window.
 */
public class App extends Application {
    private static Logger logger = LoggerFactory.getLogger(App.class);

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        AppFactory appFactory = new AppFactory(stage);
        final MainPresenter mainPresenter = appFactory.getMainPresenter();

        Scene scene = new Scene(mainPresenter.getView());
        scene.getStylesheets().add("ezxpns.css");
        stage.setScene(scene);
        stage.setTitle("EzXpns");
        stage.getIcons().add(new Image("ezicon.png"));

        addKeyboardShortcuts(scene, mainPresenter);

        // Set the window size relative to screen size
        Screen screen = Screen.getPrimary();
        Rectangle2D bounds = screen.getVisualBounds();
        stage.setMinWidth(400);
        stage.setMinHeight(300);
        stage.setWidth(bounds.getWidth() / 1.9);
        stage.setHeight(bounds.getHeight() / 1.5);

        stage.show();
    }

    /**
     * Attaches keyboard combination events to the main {@code Scene} and links them to methods in {@code
     * MainPresenter}.
     *
     * @param scene         the {@code Scene} to attach these shortcuts to.
     * @param mainPresenter the {@code mainPresenter} to activate on keyboard shortcuts.
     */
    private void addKeyboardShortcuts(Scene scene, final MainPresenter mainPresenter) {
        ArrayList<Pair<KeyCombination, String>> mappings = new ArrayList<>();

        // Functions
        KeyCombination undoCombination = new KeyCodeCombination(KeyCode.Z, KeyCombination.CONTROL_DOWN);
        KeyCombination addExpenseCombination = new KeyCodeCombination(KeyCode.N, KeyCombination.CONTROL_DOWN);
        KeyCombination generateReportCombination = new KeyCodeCombination(KeyCode.R, KeyCombination.CONTROL_DOWN);

        // Selected expenses
        KeyCombination editSelectedExpenseCombination = new KeyCodeCombination(KeyCode.E, KeyCombination.CONTROL_DOWN);
        KeyCombination deleteSelectedExpenseCombination = new KeyCodeCombination(KeyCode.DELETE);

        // Focus text fields
        KeyCombination gotoCLICombination = new KeyCodeCombination(KeyCode.SEMICOLON, KeyCombination.CONTROL_DOWN);
        KeyCombination gotoSearchFieldCombination = new KeyCodeCombination(KeyCode.F, KeyCombination.CONTROL_DOWN);
        KeyCombination gotoSearchFieldCombination2 = new KeyCodeCombination(KeyCode.K, KeyCombination.CONTROL_DOWN);
        KeyCombination gotoSearchFieldCombination3 = new KeyCodeCombination(KeyCode.F3);
        KeyCombination gotoBudgetField = new KeyCodeCombination(KeyCode.B, KeyCombination.CONTROL_DOWN);

        mappings.add(new Pair<KeyCombination, String>(undoCombination, "handleUndoButton"));
        mappings.add(new Pair<KeyCombination, String>(addExpenseCombination, "handleAddExpense"));
        mappings.add(new Pair<KeyCombination, String>(generateReportCombination, "handleGenerateReport"));

        mappings.add(new Pair<KeyCombination, String>(editSelectedExpenseCombination, "handleEditSelectedExpense"));
        mappings.add(new Pair<KeyCombination, String>(deleteSelectedExpenseCombination, "handleDeleteSelectedExpense"));

        mappings.add(new Pair<KeyCombination, String>(gotoCLICombination, "handleGotoCommandLine"));
        mappings.add(new Pair<KeyCombination, String>(gotoSearchFieldCombination, "handleGotoSearchField"));
        mappings.add(new Pair<KeyCombination, String>(gotoSearchFieldCombination2, "handleGotoSearchField"));
        mappings.add(new Pair<KeyCombination, String>(gotoSearchFieldCombination3, "handleGotoSearchField"));
        mappings.add(new Pair<KeyCombination, String>(gotoBudgetField, "handleGotoBudgetField"));

        // Linking, invokes method based off mapping
        for (final Pair<KeyCombination, String> keyMapping : mappings) {
            scene.addEventHandler(KeyEvent.KEY_PRESSED, new javafx.event.EventHandler<KeyEvent>() {
                @Override
                public void handle(KeyEvent event) {
                    if (keyMapping.getKey().match(event)) {
                        try {
                            Method method = mainPresenter.getClass().getDeclaredMethod(keyMapping.getValue());
                            logger.debug("Key combination: {} calling {}", keyMapping.getKey(), method);
                            method.invoke(mainPresenter);
                        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
                                | NoSuchMethodException | SecurityException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
        }
    }
}
